package cn.duyo.spring.beans.factory.support;

import cn.duyo.spring.beans.BeansException;
import cn.duyo.spring.beans.PropertyValue;
import cn.duyo.spring.beans.PropertyValues;
import cn.duyo.spring.beans.factory.*;
import cn.duyo.spring.beans.factory.config.*;
import cn.duyo.spring.core.convert.ConversionService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * "表示对象具有通过依赖注入自动完成依赖项绑定的能力", 只实现创建bean
 * @author du
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    // 默认Cglib实例化
    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {

        /*try {
            // TODO 此处若有构造函数入参会异常，所以需要考虑JDK、Cglib、ASM等实例化对象策略
            //bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }*/
        /**
         * 短路操作, AOP代理时机, 判断是否返回代理对象
         *  1. 当用户自定义TargetSource将会在实例化前进行代理，此时的TargetSource直接返回需要被代理的Bean，也就是说该被代理的Bean的实例化初始化操作均由自己负责。并进行短路操作
         *  2. 用户不自定义TargetSource时则返回空，在初始化后才进行AOP代理
         */
        //https://blog.csdn.net/chaitoudaren/article/details/105278868 自动代理源码解析
        // 判断是否返回代理 Bean 对象
        Object bean = resolveBeforeInstantiation(beanName, beanDefinition);
        if (null != bean) {
            return bean;
        }
        return doCreateBean(beanName, beanDefinition, args);
    }

    protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        Object bean;
        try {
            // 1.创建实例
            bean = createBeanInstance(beanDefinition, beanName, args);

            // TODO 加入处理循环依赖的逻辑，将实例化后的Bean对象提前放入缓存中暴露出来
            if (beanDefinition.isSingleton()) {
                Object finalBean = bean;
                // TODO 注意这里注册的是一个接口(即回调，在后面getSingleton(beanName)时，才会触发这个回调执行)
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));
            }

            // 实例化后判断 (返回 false 的对象，不再执行后续设置 Bean 对象属性的操作)
            boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean);
            if (!continueWithPropertyPopulation) {
                return bean;
            }

            // 在设置 Bean 属性之前，允许 BeanPostProcessor 修改属性值
            applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);

            // 2.实例化后, 给 bean 填充属性
            applyPropertyValues(beanName, bean, beanDefinition);

            // 3.执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法，AOP代理对象就是此时通过后置处理器执行
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        /**
         * 注册实现了 DisposableBean 接口的 Bean 对象
         *
         * 在创建 Bean 对象的实例的时候，需要把销毁方法保存起来，方便后续执行销毁动作进行调用。
         * 会被注册到 DefaultSingletonBeanRegistry 中新增加的 Map<String, DisposableBean> disposableBeans 属性中去，
         * 这个接口的方法最终可以被类 AbstractApplicationContext#close 方法通过 getBeanFactory().destroySingletons() 调用
         */
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);


        /**
         * 判断单例，如果是单例则加入单例池，不是则每次获取都会重新创建bean
         * 另外非 Singleton 类型的 Bean 不需要执行销毁方法。
         * TODO 如果是AOP代理类，beanName不会变，但缓存的实例则是AOP代理后的实例
         */
        Object exposedObject = bean;
        if (beanDefinition.isSingleton()) {
            /**
             * TODO
             *  1. addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean))
             *     首先得到一个ObjectFactory，注册到三级缓存
             *  2. 等待初始化化、属性填充、以及后置处理器创建代理对象
             *  3. 在属性填充时，其属性依赖B的对象，此时还没有执行后置处理器去进行AOP代理
             *     会执行 getEarlyBeanReference，进行AOP代理，此时有一个beanName缓存 earlyProxyReferences来标记是否已进行AOP代理，在后续执行后置处理器时不会再次AOP
             *  3. getSingleton(beanName)
             *     此时一级缓存没有，二级缓存也没有，三级缓存执行getObject，拿到对象后(可能被代理，也可能没有)，添加到二级缓存，移除三级缓存
             *  4. registerSingleton(beanName, exposedObject)
             *     此时二级缓存已经是成品对象，执行后将二级缓存移除，添加到一级缓存
             *  5. 循环依赖时，会再次执行 getSingleton(beanName)
             *     此时是递归，A->B，a实例在给B类型属性赋值时，发现需要初始化，则进入B的 doCreateBean 流程。
             *     B中属性a赋值发现需要a的对象，则进入getSingleton，(注意此时a的半成品对象已经进入了三级缓存)，会执行将a的AOP代理，并从三级缓存放到二级缓存，注意此时a原始对象并还未完成初始化和剩余属性的赋值，但此时a的AOP对象在二级缓存
             *     但b对象已经完成整个实例化与初始化，b对象的bean已经被放到了一级缓存。唯独其中a属性的值是个半成品。
             *     此时递归回到a原始对象的赋值和初始化，在执行 getSingleton会从二级缓存获取到a的AOP对象，其代理的原始对象a也已完成初始化，最后从二级移动到一级缓存
             */
            exposedObject = getSingleton(beanName);
            registerSingleton(beanName, exposedObject);
        }
        return exposedObject;
    }

    // TODO 实际会执行 DefaultAdvisorAutoProxyCreator.getEarlyBeanReference()，提前将半成品对象放到缓存
    protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
        System.out.println("2. 执行获取代理类缓存，beanName=" + beanName);
        Object exposedObject = bean;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                exposedObject = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(exposedObject, beanName);
                if (null == exposedObject) return exposedObject;
            }
        }
        return exposedObject;
    }


    /**
     * 在设置 Bean 属性之前，允许 BeanPostProcessor 修改属性值 (@Value)
     * @param beanName
     * @param bean
     * @param beanDefinition
     */
    protected void applyBeanPostProcessorsBeforeApplyingPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor){
                PropertyValues pvs = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).postProcessPropertyValues(beanDefinition.getPropertyValues(), bean, beanName);
                if (null != pvs) {
                    for (PropertyValue propertyValue : pvs.getPropertyValues()) {
                        beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                    }
                }
            }
        }
    }


    /**
     * 对比spring的原始代码
     */
//    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
//        Object bean = null;
//        // 确认bean在初始化阶段之前
//        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
//            // Make sure bean class is actually resolved at this point.
//            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//                // 确认当前beanName所要返回的最终类型
//                Class<?> targetType = determineTargetType(beanName, mbd);
//                if (targetType != null) {
//                    // 只有前置处理返回的bean不为null, 才进行初始化后置处理(Aop的代理在初始化后置处理中进行)
//                    // 但是除非bean自定义了TargetSource，否则前置处理返回的bean为空
//                    // 一般没有自定义TargetSource情况下，是不会在实例化前调用该后置处理，也不会导致后续短路操作！
//                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
//                    if (bean != null) {
//                        // 实例化后置处理
//                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
//                    }
//                }
//            }
//            mbd.beforeInstantiationResolved = (bean != null);
//        }
//        return bean;
//    }

    protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) {
        Object bean = applyBeanPostProcessorsBeforeInstantiation(beanDefinition.getBeanClass(), beanName);
        // 注意是前置处理并没有创建AOP对象时，直接返回null；如果创建了则还需要执行后置处理器
        if (null != bean) {
            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        return bean;
    }

    // 注意，此方法为新增方法，与 “applyBeanPostProcessorsBeforeInitialization” 是两个方法，专门用来处理AOP代理类的生成
    public Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            if (processor instanceof InstantiationAwareBeanPostProcessor) {
                Object result = ((InstantiationAwareBeanPostProcessor)processor).postProcessBeforeInstantiation(beanClass, beanName);
                if (null != result) return result;
            }
        }
        return null;
    }

    /**
     * Bean 实例化后对于返回 false 的对象，不在执行后续设置 Bean 对象属性的操作
     *
     * @param beanName
     * @param bean
     * @return
     */
    private boolean applyBeanPostProcessorsAfterInstantiation(String beanName, Object bean) {
        boolean continueWithPropertyPopulation = true;
        for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {
            if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor instantiationAwareBeanPostProcessor = (InstantiationAwareBeanPostProcessor) beanPostProcessor;
                if (!instantiationAwareBeanPostProcessor.postProcessAfterInstantiation(bean, beanName)) {
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
        return continueWithPropertyPopulation;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor<?> matchedConstructor = null;
        Class<?> beanClass = beanDefinition.getBeanClass();

        // 注意构造函数可能会有很多, 要选择参数数量匹配的那一个
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor<?> ctor : declaredConstructors) {
            if (null != args && ctor.getParameters().length == args.length) {
                matchedConstructor = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, matchedConstructor, args);
    }

    /**
     * Bean 属性填充
     */
    protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {

            /**
             * TODO 此处属性名会有重复的情况
             * 1. 如果是配置的属性有重复，由于是list保存，会进行覆盖
             * 2. 如果是 BeanFactoryPostProcessor 扩展添加了同名属性，同上
             */
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {

                String name = propertyValue.getName();
                Object value = propertyValue.getValue();

                // 遇到 BeanReference 类型, 需要递归获取bean实例 (getBean会内部判断是否实例化并调用createBean进行递归)
                if (value instanceof BeanReference) {
                    // A 依赖 B, 获取 B 的实例化
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                // 类型转换
                else {
                    Class<?> sourceType = value.getClass();
                    Class<?> targetType = (Class<?>) TypeUtil.getFieldType(bean.getClass(), name);
                    ConversionService conversionService = getConversionService();
                    if (conversionService != null) {
                        if (conversionService.canConvert(sourceType, targetType)) {
                            value = conversionService.convert(value, targetType);
                        }
                    }
                }
                // 反射填充属性
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("Error setting property values：" + beanName);
        }
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {

        // invokeAwareMethods
        /**
         * 注意这里并没有判断 ApplicationContextAware、ApplicationEventPublisherAware、ResourceLoaderAware、EnvironmentAware、MessageSourceAware等
         * 是由于此处压根拿不到ApplicationContext上下文对象，只能在后续执行后置处理器 ApplicationContextAwareProcessor这个包装类时把上下文设置回去，无需额外特殊操作
         * 详见：ApplicationContextAwareProcessor#postProcessBeforeInitialization
         */
        if(bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware){
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }

        // 1. 执行 BeanPostProcessor Before 处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);

        // 2. 执行 Bean 对象的初始化方法
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }

        // 3. 执行 BeanPostProcessor After 处理
        // TODO 此处来执行AOP代理逻辑，但要bean提前存在，所以放到后置处理器方法来创建代理类
        wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        return wrappedBean;
    }

    @SuppressWarnings({"unchecked"})
    private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
        // 1. 实现接口 InitializingBean (还有一个注解 @PostConstruct)
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }

        // 2. 反射调用 init-method {判断是为了避免二次执行销毁}
        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotEmpty(initMethodName)) {
            Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (null == initMethod) {
                throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");
            }
            initMethod.invoke(bean);
        }
    }

    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        // 非 Singleton 类型的 Bean 不需要执行销毁方法（一旦方法执行完，垃圾回收根搜索算法会标记有引用）
        if (!beanDefinition.isSingleton()) return;

        if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (null == current) return result;
            result = current;
        }
        return result;
    }
}
